﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MetaData;
using System.Drawing;
using Common;
using System.Threading;
using System.Windows.Forms;

namespace OnlineDealer
{

    public partial class OnlineDealer:IDealer
    {
        const int  PotNumberWidth = 6;
        Random rand = new Random();
        MetaDataBag DataBag;
        AppWindow table;
        Bitmap currentFrame;

        DecisionPointInfo lastData;

        HandInfo handData;
        Seat fuxiSeat = Seat.Unknown;

        public OnlineDealer(AppWindow window)
        {
            DataBag = MetaDataBag.Load();
            table = window;
            table.BringWindowToFront();
            table.Restore(new Rectangle(table.Rect.X, table.Rect.Y,
                table.Rect.X + DataBag.TableSize.Width - 1,
                table.Rect.Y + DataBag.TableSize.Height - 1));
            CurrentInfo = new DecisionPointInfo();
            lastData = new DecisionPointInfo();
            AllHands = new List<HandInfo>();
        }
        public void StartDealing()
        {
            for (; ; )
            {

                lastData = CurrentInfo;
                CurrentInfo = new DecisionPointInfo();
                currentFrame = Utility.CaptureWindow(table);
                ProcessFrameData();
                currentFrame.Dispose();
                Thread.Sleep(700);
            }
        }
        void ProcessFrameData()
        {
            CurrentInfo.LeftPocket = GetLeftPocket();
            CurrentInfo.RightPocket = GetRightPocket();
            CurrentInfo.CanFold = GetFoldButtonState();
            if (fuxiSeat == Seat.Unknown)
            {
                if (!String.IsNullOrEmpty(CurrentInfo.LeftPocket) && CurrentInfo.CanFold)
                {
                    fuxiSeat = Seat.Left;
                }
                else if (!String.IsNullOrEmpty(CurrentInfo.RightPocket) && CurrentInfo.CanFold)
                {
                    fuxiSeat = Seat.Right;
                }
            }

            if (fuxiSeat != Seat.Unknown)
            {
                CurrentInfo.LeftDealer = GetLeftDealer();
                CurrentInfo.RightDealer = GetRightDealer();
                if (handData == null)
                {
                    CurrentInfo.NewHand = CurrentInfo.LeftDealer || CurrentInfo.RightDealer;
                }
                else
                {
                    CurrentInfo.NewHand = handData.DealerSeat == Seat.Left && CurrentInfo.RightDealer ||
                                          handData.DealerSeat == Seat.Right && CurrentInfo.LeftDealer;
                }
                if (CurrentInfo.NewHand)
                {
                    if (handData != null)
                    {
                        if (handData.Actions.Count == 0)
                        {
                            if (handData.DealerSeat == fuxiSeat)
                            {
                                handData.Actions.Add(new PlayerAction { PlayerId = G.FuxiPlayerID, Round = handData.LastRound, Type = ActionType.Fold });
                            }
                            else
                            {
                                handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = handData.LastRound, Type = ActionType.Fold });
                            }
                        }
                        else
                        {
                            if (handData.Actions[handData.Actions.Count - 1].PlayerId == G.FuxiPlayerID && handData.Actions[handData.Actions.Count - 1].Type == ActionType.Raise)
                            {
                                handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = G.River, Type = ActionType.Call });
                            }
                            if (handData.Actions[handData.Actions.Count - 1].PlayerId == G.FuxiPlayerID && handData.Actions[handData.Actions.Count - 1].Type == ActionType.Check && !handData.HavePosition)
                            {
                                handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = G.River, Type = ActionType.Check });
                            }
                        }
                    }
                    handData = new HandInfo();
                    handData.DealerSeat = CurrentInfo.LeftDealer ? Seat.Left : Seat.Right;
                    handData.HavePosition = handData.DealerSeat == fuxiSeat;
                    handData.BigBlind = GetBigBlind();
                    handData.FirstPreflopPass = true;
                    AllHands.Insert(0, handData);
                    OnNewHandStarted();
                }

                CurrentInfo.Board = GetBoard();
                if(String.IsNullOrEmpty(handData.Board)||
                    CurrentInfo.Board.Length > handData.Board.Length)
                {
                    handData.Board = CurrentInfo.Board;
                }
                if (CurrentInfo.Board.Length == 0)
                {
                    CurrentInfo.Round = G.Preflop;
                }
                else
                {
                    CurrentInfo.Round = (CurrentInfo.Board.Length - 4) / 2;
                }
                if (CurrentInfo.Round > handData.LastRound)
                {
                    handData.LastRound = CurrentInfo.Round;
                    LogMissedOppActions();
                }
                if (LeftFolded)
                {
                    if (!(handData.Actions.Count > 0 && handData.Actions[handData.Actions.Count - 1].Type == ActionType.Fold))
                    {
                        if (fuxiSeat == Seat.Left)
                        {
                            handData.Actions.Add(new PlayerAction { PlayerId = G.FuxiPlayerID, Round = handData.LastRound, Type = ActionType.Fold });
                        }
                        else
                        {
                            handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = handData.LastRound, Type = ActionType.Fold });
                        }
                    }
                }
                if (RightFolded)
                {
                    if (!(handData.Actions.Count > 0 && handData.Actions[handData.Actions.Count - 1].Type == ActionType.Fold))
                    {
                        if (fuxiSeat == Seat.Left)
                        {
                            handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = handData.LastRound, Type = ActionType.Fold });
                        }
                        else
                        {
                            handData.Actions.Add(new PlayerAction { PlayerId = G.FuxiPlayerID, Round = handData.LastRound, Type = ActionType.Fold });
                        }
                    }
                }

                if (CurrentInfo.CanFold)
                {
                    PopulateFullData();
                    OnDecisionPoint();
                    handData.FirstPreflopPass = false;
                }
            }
        }
        private void PopulateFullData()
        {
            if (String.IsNullOrEmpty(handData.FuxiPocket))
            {
                if (fuxiSeat == Seat.Left)
                {
                    handData.FuxiPocket = CurrentInfo.LeftPocket;
                }
                else if (fuxiSeat == Seat.Right)
                {
                    handData.FuxiPocket = CurrentInfo.RightPocket;
                }
            }
            CurrentInfo.CanCheck = GetCheckButtonState();
            CurrentInfo.CanCall = GetCallButtonState();
            CurrentInfo.CanRaise = GetRaiseButtonState();
            if (handData.HavePosition)
            {
                if (!CurrentInfo.CanCheck)
                {
                    if (!handData.FirstPreflopPass)
                    {
                        handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round, Type = ActionType.Raise });
                    }
                }
                else
                {
                    handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round, Type = ActionType.Check });
                }
            }
            else
            {
                if (!CurrentInfo.CanCheck)
                {
                    handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round, Type = ActionType.Raise });
                }
                else
                {
                    if (handData.FirstPreflopPass)
                    {
                        handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round, Type = ActionType.Call });
                    }
                }
            }
            CalcPotOddsInfo();
        }

        private void LogMissedOppActions()
        {
            if (handData.Actions.Count > 0) //Avoid all-in exception
            {
                var lastAction = handData.Actions[handData.Actions.Count - 1];
                if (lastAction.PlayerId == G.FuxiPlayerID && lastAction.Type == ActionType.Raise)
                {
                    handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round - 1, Type = ActionType.Call });
                }
                else if (lastAction.PlayerId == G.FuxiPlayerID && lastAction.Type == ActionType.Check && !handData.HavePosition && CurrentInfo.Round != G.Preflop ||
                    lastAction.PlayerId == G.FuxiPlayerID && lastAction.Type == ActionType.Call && handData.Actions.Count == 1)
                {
                    handData.Actions.Add(new PlayerAction { PlayerId = "opp", Round = CurrentInfo.Round - 1, Type = ActionType.Check });
                }
            }
        }

        private void CalcPotOddsInfo()
        {
            if (CurrentInfo.Round == G.Preflop)
            {
                if (handData.HavePosition && handData.FirstPreflopPass)
                {
                    CurrentInfo.ChipsToCall = 0.5;
                }
                else
                {
                    CurrentInfo.ChipsToCall = 1;
                }
            }
            else if (CurrentInfo.Round == G.Flop)
            {
                CurrentInfo.ChipsToCall = 1;
            }
            else if (CurrentInfo.Round == G.Turn)
            {
                CurrentInfo.ChipsToCall = 2;
            }
            else if (CurrentInfo.Round == G.River)
            {
                CurrentInfo.ChipsToCall = 2;
            }
            CurrentInfo.Pot = GetPot() / handData.BigBlind;
        }
        bool GetLeftDealer()
        {
            int hash = currentFrame.GetPixel(DataBag.LeftDealerLocation.X,DataBag.LeftDealerLocation.Y).ToArgb();
            return hash == DataBag.LeftDealerHash;
        }
        bool GetRightDealer()
        {
            int hash = currentFrame.GetPixel(DataBag.RightDealerLocation.X, DataBag.RightDealerLocation.Y).ToArgb();
            return hash == DataBag.RightDealerHash;
        }
        string GetBoard()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 5; i++)
            {
                int hash = Utility.GetRegionHash(currentFrame,new Rectangle(DataBag.CommonCards[i], DataBag.BoardCardSize));
                if (DataBag.BoardCardHashes.ContainsKey(hash))
                {
                    sb.Append(DataBag.BoardCardHashes[hash].Substring(0,2));
                }    
            }
            return sb.ToString();
        }
        double GetBigBlind()
        {
            int delimitor = table.Title.IndexOf("/");
            string s = table.Title.Substring(delimitor + 1);
            s.Trim();
            delimitor = s.IndexOf(" ");
            if (delimitor != -1)
            {
                s = s.Substring(0, delimitor);
            }
            if (s[0] == '$')
            {
                s = s.Substring(1);
            }
            return Double.Parse(s) / 2;
        }
        double GetPot()
        {
            StringBuilder sb = new StringBuilder();
            int x = DataBag.PotRect.X;
            while (x < DataBag.PotRect.X + DataBag.PotRect.Width - PotNumberWidth)
            {
                long dotHash = Utility.GetFrontHash(currentFrame, new Rectangle(x, DataBag.PotRect.Y, 3, DataBag.PotRect.Height), DataBag.PotFontColor);
                if (DataBag.PotNumberHashes.ContainsKey(dotHash) && DataBag.PotNumberHashes[dotHash]=="dot")
                {
                    sb.Append(".");
                    x+=2;
                }
                else
                {
                    long hash = Utility.GetFrontHash(currentFrame, new Rectangle(x, DataBag.PotRect.Y, PotNumberWidth, DataBag.PotRect.Height), DataBag.PotFontColor);
                    if (DataBag.PotNumberHashes.ContainsKey(hash))
                    {
                        sb.Append(DataBag.PotNumberHashes[hash]);
                        x += PotNumberWidth;
                    }
                    else
                    {
                        x++;
                    }
                }
            }
            if (sb.Length != 0)
                return Double.Parse(sb.ToString());
            else
                return 0;
        }
        string GetLeftPocket()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 2; i++)
            {
                int hash = Utility.GetRegionHash(currentFrame, new Rectangle(DataBag.LeftCards[i], DataBag.HoleCardSize));
                if (DataBag.HoleCardHashes.ContainsKey(hash))
                {
                    sb.Append(DataBag.HoleCardHashes[hash].Substring(0, 2));
                }
            }
            return sb.ToString();
        }
        string GetRightPocket()
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 2; i++)
            {
                int hash = Utility.GetRegionHash(currentFrame, new Rectangle(DataBag.RightCards[i], DataBag.HoleCardSize));
                if (DataBag.HoleCardHashes.ContainsKey(hash))
                {
                    sb.Append(DataBag.HoleCardHashes[hash].Substring(0, 2));
                }
            }
            return sb.ToString();
        }
        bool GetLeftFoldState()
        {
            int hash = Utility.GetRegionHash(currentFrame, DataBag.LeftFoldRect);
            return hash == DataBag.LeftFoldHash;
        }
        bool LeftFolded
        {
            get
            {
                return DataBag.LeftFoldHash == Utility.GetRegionHash(currentFrame, DataBag.LeftFoldRect);
            }
        }
        bool RightFolded
        {
            get
            {
                return DataBag.RightFoldHash == Utility.GetRegionHash(currentFrame, DataBag.RightFoldRect);
            }
        }
        bool GetFoldButtonState()
        {
            int hash = Utility.GetRegionHash(currentFrame, DataBag.FoldButtonRect);
            return hash == DataBag.FoldButtonHash;
        }
        bool GetCheckButtonState()
        {
            int hash = Utility.GetRegionHash(currentFrame, DataBag.CheckButtonRect);
            return hash == DataBag.CheckButtonHash;
        }
        bool GetCallButtonState()
        {
            int hash = Utility.GetRegionHash(currentFrame, DataBag.CallButtonRect);
            return hash == DataBag.CallButtonHash;
        }
        bool GetRaiseButtonState()
        {
            int betHash = Utility.GetRegionHash(currentFrame, DataBag.BetButtonRect);
            int raiseHash = Utility.GetRegionHash(currentFrame, DataBag.RaiseButtonRect);
            return DataBag.BetButtonHash == betHash || DataBag.RaiseButtonHash == raiseHash;
        }


        #region IDealer interface members

        public event EventHandler NewHandStarted;
        public event EventHandler<DecisionEventArgs> DecisionPoint;
        public DecisionPointInfo CurrentInfo { get; private set; }
        public List<HandInfo> AllHands { get; private set; }
        protected virtual void OnNewHandStarted()
        {
            if (NewHandStarted != null)
            {
                NewHandStarted(this, new EventArgs());
            }
        }
        protected virtual void OnDecisionPoint()
        {
            if (DecisionPoint != null)
            {
                DecisionPoint(this, new DecisionEventArgs(G.FuxiPlayerID, CurrentInfo));
            }
        }
        public void SetupGame()
        {
        }
        #endregion
    }
}
